 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font should be
  * activated and used by other components.
  *******************************************************************************/
 package org.eclipse.ui.internal.ide.dialogs;

 import java.lang.reflect.InvocationTargetException ;

 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.wizard.IWizard;
 import org.eclipse.jface.wizard.IWizardContainer;
 import org.eclipse.jface.wizard.IWizardPage;
 import org.eclipse.jface.wizard.WizardPage;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
 import org.eclipse.ui.internal.ide.misc.WizardStepGroup;

 /**
  * This page allows the user to go thru a series of steps. Each
  * step that has a wizard will have its pages embedded into this
  * page. At the end, the step will be asked to complete its work.
  * <p>
  * Example usage:
  * <pre>
  * mainPage = new MultiStepConfigureWizardPage("multiStepWizardPage");
  * mainPage.setTitle("Project");
  * mainPage.setDescription("Configure project capability.");
  * </pre>
  * </p>
  */
 public class MultiStepConfigureWizardPage extends WizardPage {
     private MultiStepWizardDialog wizardDialog;

     private Composite pageSite;

     private WizardStepGroup stepGroup;

     private WizardStepContainer stepContainer = new WizardStepContainer();

     /**
      * Creates a new multi-step wizard page.
      *
      * @param pageName the name of this page
      */
     public MultiStepConfigureWizardPage(String pageName) {
         super(pageName);
     }

     /* (non-Javadoc)
      * Method declared on IWizardPage
      */
     public boolean canFlipToNextPage() {
         return stepContainer.canFlipToNextPage();
     }

     /* (non-Javadoc)
      * Method declared on IDialogPage.
      */
     public void createControl(Composite parent) {
         Composite composite = new Composite(parent, SWT.NULL);
         GridLayout layout = new GridLayout();
         layout.numColumns = 2;
         composite.setLayout(layout);
         composite.setLayoutData(new GridData(GridData.FILL_BOTH));
         composite.setFont(parent.getFont());

         PlatformUI.getWorkbench().getHelpSystem().setHelp(composite,
                 IIDEHelpContextIds.NEW_PROJECT_CONFIGURE_WIZARD_PAGE);

         createStepGroup(composite);
         createEmbeddedPageSite(composite);

         setControl(composite);
     }

     /**
      * Creates the control where the step's wizard will
      * display its pages.
      */
     private void createEmbeddedPageSite(Composite parent) {
         pageSite = new Composite(parent, SWT.NONE);
         pageSite.setLayout(new GridLayout());
         pageSite.setLayoutData(new GridData(GridData.FILL_BOTH));
     }

     /**
      * Creates the control for the step list
      */
     private void createStepGroup(Composite parent) {
         stepGroup = new WizardStepGroup();
         stepGroup.createContents(parent);
     }

     /**
      * Returns the container handler for the pages
      * of the step's wizard.
      */
     /* package */WizardStepContainer getStepContainer() {
         return stepContainer;
     }

     /* (non-Javadoc)
      * Method declared on IDialogPage.
      */
     public String getMessage() {
         String msg = stepContainer.getMessage();
         if (msg == null || msg.length() == 0) {
             msg = super.getMessage();
         }
         return msg;
     }

     /* (non-Javadoc)
      * Method declared on IWizardPage.
      */
     public IWizardPage getPreviousPage() {
         return stepContainer.getPreviousPage();
     }

     /* (non-Javadoc)
      * Method declared on IWizardPage.
      */
     public void setPreviousPage(IWizardPage page) {
         // Do not allow to go back
 super.setPreviousPage(null);
     }

     /**
      * Sets the steps to be displayed. Ignored if the
      * createControl has not been called yet.
      *
      * @param steps the collection of steps
      */
     /* package */void setSteps(WizardStep[] steps) {
         if (stepGroup != null) {
             stepGroup.setSteps(steps);
         }
     }

     /**
      * Sets the multi-step wizard dialog processing this
      * page.
      */
     /* package */void setWizardDialog(MultiStepWizardDialog dialog) {
         wizardDialog = dialog;
     }

     /* (non-Javadoc)
      * Method declared on IDialogPage.
      */
     public void setVisible(boolean visible) {
         super.setVisible(visible);
         WizardStep[] steps = stepGroup.getSteps();
         MultiStepWizard stepWizard = wizardDialog.getMultiStepWizard();
         wizardDialog.setFinishLabel(stepWizard.getFinishStepLabel(steps));

         getControl().getDisplay().asyncExec(new Runnable () {
             public void run() {
                 stepContainer.processCurrentStep();
             }
         });
     }

     /**
      * Support for handling pages from the step's wizard
      */
     /* package */class WizardStepContainer implements IWizardContainer {
         private int stepIndex = 0;

         private IWizard wizard;

         private IWizardPage currentPage;

         /* (non-Javadoc)
          * Method declared on IRunnableContext.
          */
         public void run(boolean fork, boolean cancelable,
                 IRunnableWithProgress runnable)
                 throws InvocationTargetException , InterruptedException {
             getContainer().run(fork, cancelable, runnable);
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public IWizardPage getCurrentPage() {
             return currentPage;
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public Shell getShell() {
             return getContainer().getShell();
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public void showPage(IWizardPage page) {
             showPage(page, true);
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public void updateButtons() {
             getContainer().updateButtons();
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public void updateMessage() {
             getContainer().updateMessage();
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public void updateTitleBar() {
             getContainer().updateTitleBar();
         }

         /* (non-Javadoc)
          * Method declared on IWizardContainer.
          */
         public void updateWindowTitle() {
             getContainer().updateWindowTitle();
         }

         /**
          * Handles the back button pressed
          */
         public void backPressed() {
             showPage(currentPage.getPreviousPage(), false);
         }

         /**
          * Handles the next button pressed
          */
         public void nextPressed() {
             showPage(currentPage.getNextPage(), true);
         }

         /**
          * Handles the help button pressed
          */
         public void helpPressed() {
             if (currentPage != null) {
                 currentPage.performHelp();
             }
         }

         /**
          * Handles close request.
          *
          * @return the result
          */
         public final boolean performCancel() {
             if (wizard != null) {
                 return wizard.performCancel();
             }

             return true;
         }

         /**
          * Handles finish request.
          *
          * @return the result
          */
         public final boolean performFinish() {
             if (wizard != null) {
                 if (wizard.performFinish()) {
                     wizard.dispose();
                     wizard.setContainer(null);
                     stepGroup.markStepAsDone();
                     stepIndex++;
                     return true;
                 }
                 return false;
             }
             return true;
             
         }

         /**
          * Calculates the difference in size between the given
          * page and the page site. A larger page results
          * in a positive delta.
          *
          * @param page the page
          * @return the size difference encoded
          * as a <code>new Point(deltaWidth,deltaHeight)</code>
          */
         private Point calculatePageSizeDelta(IWizardPage page) {
             Control pageControl = page.getControl();

             if (pageControl == null) {
                 // control not created yet
 return new Point(0, 0);
             }

             Point contentSize = pageControl.computeSize(SWT.DEFAULT,
                     SWT.DEFAULT, true);
             Rectangle rect = pageSite.getClientArea();
             Point containerSize = new Point(rect.width, rect.height);

             return new Point(Math.max(0, contentSize.x - containerSize.x), Math
                     .max(0, contentSize.y - containerSize.y));
         }

         /**
          * Computes the correct page site size for the given page
          * and resizes the dialog if nessessary.
          *
          * @param page the wizard page
          */
         private void updateSizeForPage(IWizardPage page) {
             // ensure the page container is large enough
 Point delta = calculatePageSizeDelta(page);

             if (delta.x > 0 || delta.y > 0) {
                 Point siteSize = pageSite.getSize();
                 GridData data = (GridData) pageSite.getLayoutData();
                 data.heightHint = siteSize.y + delta.y;
                 data.widthHint = siteSize.x + delta.x;
             }
         }

         /**
          * Computes the correct page site size for the given wizard
          * and resizes the dialog if nessessary.
          *
          * @param wizard the wizard
          */
         private void updateSizeForWizard(IWizard wizard) {
             Point delta = new Point(0, 0);
             IWizardPage[] pages = wizard.getPages();
             for (int i = 0; i < pages.length; i++) {
                 // ensure the page site is large enough
 Point pageDelta = calculatePageSizeDelta(pages[i]);

                 delta.x = Math.max(delta.x, pageDelta.x);
                 delta.y = Math.max(delta.y, pageDelta.y);
             }

             if (delta.x > 0 || delta.y > 0) {
                 Point siteSize = pageSite.getSize();
                 GridData data = (GridData) pageSite.getLayoutData();
                 data.heightHint = siteSize.y + delta.y;
                 data.widthHint = siteSize.x + delta.x;
             }
         }

         /**
          * Process the current step's wizard.
          */
         public void processCurrentStep() {
             WizardStep[] steps = stepGroup.getSteps();
             while (stepIndex < steps.length) {
                 // adjust the finish button label
 if (stepIndex == (steps.length - 1)) {
                     wizardDialog.setFinishLabel(null);
                 }

                 final WizardStep step = steps[stepIndex];
                 stepGroup.setCurrentStep(step);

                 final IWizard[] stepWizard = new IWizard[1];
                 BusyIndicator.showWhile(getShell().getDisplay(),
                         new Runnable () {
                             public void run() {
                                 stepWizard[0] = step.getWizard();
                                 int tries = 0;
                                 while (stepWizard[0] == null && tries++ < 3) {
                                     boolean tryAgain = wizardDialog
                                             .getMultiStepWizard()
                                             .handleMissingStepWizard(step);
                                     if (!tryAgain) {
                                         break;
                                     }

                                     stepWizard[0] = step.getWizard();
                                 }
                             }
                         });

                 if (stepWizard[0] == null) {
                     break;
                 }
                 setWizard(stepWizard[0]);
                 if (stepWizard[0].getPageCount() > 0) {
                     return;
                 }

                 performFinish();
             }

             wizardDialog.forceClose();
         }

         /**
          * Sets the current wizard.
          *
          * @param newWizard the current wizard
          */
         public void setWizard(IWizard newWizard) {
             wizard = newWizard;

             // Allow the wizard pages to precreate their page controls
 // This allows the wizard to open to the correct size
 wizard.createPageControls(pageSite);

             // Ensure that all of the created pages are initially not visible
 IWizardPage[] pages = wizard.getPages();
             for (int i = 0; i < pages.length; i++) {
                 IWizardPage page = pages[i];
                 if (page.getControl() != null) {
                     page.getControl().setVisible(false);
                 }
             }

             // Ensure the dialog is large enough for the wizard
 updateSizeForWizard(wizard);
             wizardDialog.updateLayout();

             wizard.setContainer(this);
             showPage(wizard.getStartingPage(), false);
         }

         /**
          * Show the requested page.
          *
          * @param page the page
          * @param rememberPrevious whether hte previous page should be remembered
          */
         public void showPage(IWizardPage page, boolean rememberPrevious) {
             if (page == null || page == currentPage) {
                 return;
             }

             if (rememberPrevious && currentPage != null) {
                 page.setPreviousPage(currentPage);
             }

             if (wizard != page.getWizard()) {
                 throw new IllegalStateException ();
             }

             // ensure that page control has been created
 // (this allows lazy page control creation)
 if (page.getControl() == null) {
                 page.createControl(pageSite);
                 // the page is responsible for ensuring the created control is accessable
 // via getControl.
 if (page.getControl() == null) {
                     throw new IllegalArgumentException ();
                 }
                 // ensure the dialog is large enough for this page
 updateSizeForPage(page);
                 wizardDialog.updateLayout();
             }

             // make the new page visible
 IWizardPage oldPage = currentPage;
             currentPage = page;
             currentPage.setVisible(true);
             if (oldPage != null) {
                 oldPage.setVisible(false);
             }
             page.getControl().setBounds(pageSite.getClientArea());

             // update the dialog controls
 wizardDialog.updateAll();
         }

         /**
          * Returns whether the current wizard can finish.
          *
          * @return whether the wizard can finish
          */
         public boolean canWizardFinish() {
             if (wizard != null) {
                 return wizard.canFinish();
             }

             return false;
         }

         /**
          * Returns whether the current page can flip to
          * the next page.
          *
          * @return can flip to next page
          */
         public boolean canFlipToNextPage() {
             if (currentPage != null) {
                 return currentPage.canFlipToNextPage();
             }
             return false;
         }

         /**
          * Returns the current page's message.
          *
          * @return the message
          */
         public String getMessage() {
             if (currentPage != null) {
                 return currentPage.getMessage();
             }

             return null;
         }

         /**
          * Returns the current page's previous page.
          *
          * @return the page
          */
         public IWizardPage getPreviousPage() {
             if (currentPage != null) {
                 return currentPage.getPreviousPage();
             }

             return null;
         }
     }
 }

